<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
// 应用管理
class AppManage
{
	private $CI = null;
	private $app = array();// array('app_id', 'edition_id', 'id')
	
	private $destination_dir = "";	
	private $errmsg = array();	
	private $count = array('success'=>0, 'fail'=>0, 'total'=>0);
	
	private $_path = array();
	
	public function __construct($params=array())
	{
		$this->CI =& get_instance();
		$this->_path = config_item("path");
		if (!empty($params))
		{
			$this->app = $params;	
		}			
	}
	
	// 导入app zip压缩文件
	public function importApp()
	{	
		// 获得压缩包
		if(isset($_FILES['file1']) && $_FILES['file1']['error'] == 0)
		{
			// 类型检测
			if ($_FILES['file1']['type'] != 'application/zip')
			{
				return $this->showmsg("导入失败，文件格式必须为zip。", false);
			}
			
			$file_upload_path = $this->_path['local_upload'].$_FILES['file1']['name'];
			if(!move_uploaded_file($_FILES['file1']['tmp_name'], $file_upload_path))
			{			
				return $this->showmsg('文件上传失败', false);
			}
		}
		else
		{
			return $this->showmsg('压缩文件没有上传', false);
		}
		
		$this->CI->load->helper('directory');
		$this->destination_dir = get_app_version_dir($this->app['app_id'], $this->app['edition_id']);		
		if (!file_exists($this->destination_dir)) 
		{
			mkdir($this->destination_dir);
		}				
		// 解压到对应目录
		if(file_exists($file_upload_path)) 
		{
			// 覆盖已有文件，可能需要记录
			$r = $this->unzip($file_upload_path, $this->destination_dir, false, true);			
			if (!$r)
			{
				return $this->showmsg('解压文件失败', false);
			}			
			unlink($file_upload_path);
		} 
		else 
		{
			return $this->showmsg('压缩文件不存在', false);
		}
		// 导入整个文件夹到数据库			
		$files = directory_map($this->destination_dir);	
				
		if (empty($files)) 
		{
			return $this->showmsg('获取文件夹目录结构失败');
		} 
		else 
		{
			$this->iterate_dir($files);	
		}	
		
		$msg = sprintf("总共%d文件，成功导入 %d, 导入失败 %d", $this->count['total'], $this->count['success'], $this->count['fail']);
		
		return $this->showmsg("导入结果: $msg ");
	}
	// 添加应用
	public function insertApp()
	{
		$app_id = $this->CI->input->post('app_id');
		$app_title = $this->CI->input->post('app_title');
		$app_intro = $this->CI->input->post('app_intro');
		$edition_id = 'standard';
		
		// 检查应用ID是否冲突
		$query = $this->CI->db->query("SELECT * FROM applications_tbl WHERE app_id = '{$app_id}'");
		if ($query->num_rows() > 0) 
		{
			return $this->showmsg($app_id." 应用名已存在，请重新取名。", false);
		}
		
		// 如果有应用图标，则保存。
		$file_upload_path = "";
		if(isset($_FILES['app_icon']) && $_FILES['app_icon']['error'] == 0)
		{
			$file_upload_path = "./public/images/".$_FILES['app_icon']['name'];
			move_uploaded_file($_FILES['app_icon']['tmp_name'], $file_upload_path);				
		}
		
		// 添加应用
		$insert_app = array(
			'app_id'=>$app_id,
			'app_title'=>$app_title,
			'app_intro'=>$app_intro
		);
		
		if (file_exists($file_upload_path)) 
		{
			$insert_app['app_icon'] = $_FILES['app_icon']['name'];
		}
		
		$this->CI->db->insert('applications_tbl', $insert_app);				
		// 添加应用版本
		$insert_edition = array(
			'edition_id'=>$edition_id,
			'app_id'=>$app_id,
			'edition_ver'=>1,
			'edition_intro'=>'nothing',
			'add_time'=> date('Y-m-d H:i:s')	
		);
		$this->CI->db->insert('appeditions_tbl', $insert_edition);	
		
		// 创建应用目录
		$dirs = array($app_id, $edition_id);
		$cdir = $this->_path['local_version'];
		foreach($dirs as $dir) 
		{
			$cdir .= $dir.'/';
			mkdir($cdir);			
		}
		
		return $this->showmsg("新增应用成功");
	}
	
	// 删除整个应用
	public function deleteApp()
	{
		$count = array('v'=>0, 'f'=>0);		
		$this->CI->db->query("DELETE FROM applications_tbl WHERE app_id = '{$this->app['app_id']}'");
		
		$this->CI->db->query("DELETE FROM appeditions_tbl WHERE app_id = '{$this->app['app_id']}'");
		$count['v'] = $this->CI->db->affected_rows();
		
		$this->CI->db->query("DELETE FROM appfiles_tbl WHERE app_id = '{$this->app['app_id']}'");
		$count['f'] = $this->CI->db->affected_rows();
		
		$dirPath = get_app_dir($this->app['app_id']);				
		// 删除这个应用版本本地文件
		if (PHP_OS === 'Windows' || PHP_OS == 'WINNT')
		{
		    exec("rd /s /q {$dirPath}");
		}
		else
		{
		    exec("rm -rf {$dirPath}");
		}
		
		$result = sprintf("%s 应用一共删除了 <b>%d</b> 版本， 一共删除了 <b>%d</b> 文件。",$this->app['app_id'], $count['v'], $count['f']);
		return $this->showmsg("删除结果:  $result");
	}

	public function copyApp(){}

	public function moveApp(){}

	// 删除应用版本，包括数据库数据及本地文件
	public function deleteAppEdition()
	{		
		$del_num = 0;
		$del_sql = "DELETE FROM appfiles_tbl WHERE app_id = '%s' AND edition_id = '%s'";
		$this->CI->db->query(sprintf($del_sql, $this->app['app_id'], $this->app['edition_id']));
		$del_num = $this->CI->db->affected_rows();
		
		$this->CI->db->query("DELETE FROM appeditions_tbl WHERE id = ".intval($this->app['id']));			
		
		$dirPath = get_app_version_dir($this->app['app_id'], $this->app['edition_id']);				
		// 删除这个应用版本本地文件
		if (PHP_OS === 'Windows' || PHP_OS == 'WINNT')
		{
		    exec("rd /s /q {$dirPath}");
		}
		else
		{
		    exec("rm -rf {$dirPath}");
		}			
				
		return $this->showmsg(sprintf("%s应用[%s]版本删除成功，一共删除了 <b>%d</b> 文件。", $this->app['app_id'], $this->app['edition_id'], $del_num));
	}		
	
	private function showmsg($errmsg, $flag=true)
	{
		return sprintf("<span style='color: %s'>%s</span>", ($flag) ? 'green' : 'red', $errmsg);
	}
			
	private function iterate_dir($data, $pre="")
	{
		if ($data)
		{
			if (is_array($data)) 
			{				
				foreach($data as $key => $item)
				{									
					if(is_string($key) && is_array($item)) 
					{
						$pre .= $key;	
						$this->iterate_dir($item, $pre);
						$pre = substr($pre, 0, strpos($pre, $key)); //回退到上层目录
					}																	
					else
					{						
						$this->import($this->destination_dir.'/'.$pre.$item, $item, $pre.$item);						
					}										
				}			
			}						
		}
	}
	
	// 导入文件
	public function import($file_path, $filename, $relate_filepath)
	{
		++$this->count['total'];
		if (!file_exists($file_path)) 
		{
			++$this->count['fail'];
			$this->errmsg[]="<span style='color:red;'>".$file_path." not exists</span>";
		}
		else
		{					
			$file_content = file_get_contents($file_path);
			$insert = array(
				'file_title'=>'Nothing',
				'file_name' => $filename,
				'file_version'=>1,
				'file_source'=>sprintf("%s/%s/%s", $this->app['app_id'], $this->app['edition_id'], $filename),
				'file_intro'=>'Nothing',
				'file_content'=>$file_content,
				'app_id'=>$this->app['app_id'],
				'edition_id'=>$this->app['edition_id'],
				'file_type'=> substr($filename, strrpos($filename, '.')+1),
				'file_size'=>filesize($file_path),
				'file_mime_type'=>"nothing",
				'file_path' => $relate_filepath
			);		
			$id = $this->CI->db->insert('appfiles_tbl', $insert);
			($this->CI->db->insert_id() > 0) ? ++$this->count['success'] : ++$this->count['fail'];
			$this->errmsg[] = "<span style='color:green;'>".$file_path." add success.</span>";
		}				
	}
	
	/**
	 * Unzip the source_file in the destination dir
	 *
	 * @param   string      The path to the ZIP-file.
	 * @param   string      The path where the zipfile should be unpacked, if false the directory of the zip-file is used
	 * @param   boolean     Indicates if the files will be unpacked in a directory with the name of the zip-file (true) or not (false) (only if the destination directory is set to false!)
	 * @param   boolean     Overwrite existing files (true) or not (false)
	 *  
	 * @return  boolean     Succesful or not
	 */
	public function unzip($src_file, $dest_dir=false, $create_zip_name_dir=true, $overwrite=true) 
	{
		if ($zip = zip_open($src_file)) 
		{
			if ($zip) 
			{
				$splitter = ($create_zip_name_dir === true) ? "." : "/";
				if ($dest_dir === false) $dest_dir = substr($src_file, 0, strrpos($src_file, $splitter))."/";

				// Create the directories to the destination dir if they don't already exist
				$this->create_dirs($dest_dir);

				// For every file in the zip-packet
				while ($zip_entry = zip_read($zip)) 
				{
					// Now we're going to create the directories in the destination directories

					// If the file is not in the root dir
					$pos_last_slash = strrpos(zip_entry_name($zip_entry), "/");
					if ($pos_last_slash !== false)
					{
						// Create the directory where the zip-entry should be saved (with a "/" at the end)
						$this->create_dirs($dest_dir.substr(zip_entry_name($zip_entry), 0, $pos_last_slash+1));
					}

					// Open the entry
					if (zip_entry_open($zip,$zip_entry,"r")) 
					{
						// The name of the file to save on the disk
						$file_name = $dest_dir.zip_entry_name($zip_entry);

						// Check if the files should be overwritten or not
						if ($overwrite === true || $overwrite === false && !is_file($file_name))
						{
							// Get the content of the zip entry
							$fstream = zip_entry_read($zip_entry, zip_entry_filesize($zip_entry));
							if (! is_dir($file_name)) 
							{
								file_put_contents($file_name, $fstream );
								// Set the rights
								chmod($file_name, 0777);									
							}								
						}

						// Close the entry
						zip_entry_close($zip_entry);
					}       
				}
				// Close the zip-file
				zip_close($zip);
			}
		} 
		else
		{
			return false;
		}

		return true;
	}

	/**
	 * This function creates recursive directories if it doesn't already exist
	 *
	 * @param String  The path that should be created
	 *  
	 * @return  void
	 */
	private function create_dirs($path)
	{
		if (!is_dir($path))
		{
			$directory_path = "";
			$directories = explode("/",$path);
			array_pop($directories);

			foreach($directories as $directory)
			{
				$directory_path .= $directory."/";
				if (!is_dir($directory_path))
				{
					mkdir($directory_path);
					chmod($directory_path, 0777);
				}
			}
		}
	}
}
